package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.*;
import com.myk.game.gobangchess.Sign;
import com.myk.game.gobangchess.constants.BoardStyleData;
import com.myk.game.gobangchess.constants.Msger;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.*;
import com.myk.game.gobangchess.utils.GridsUtils;
import com.myk.game.gobangchess.utils.SignsUtils;

import java.util.ArrayList;
import java.util.List;


/**
 * 向黑暗挥刀（暗影挥刀棋）
 * 5X5棋盘，第一回合同时落子，第二回合开始轮流行动：
 * <p>
 * 玩家每回合有5种行动可以选
 * A.移动1次
 * B.攻击1次
 * C.移动1次再移动1次
 * D.攻击1次再移动1次
 * E.什么都不做，不发出动静
 * 特殊规则：
 * （1）上回合选择E，才能选择C、D
 * （2）不可连续选择E
 * 每次移动都会在原位发出动静
 * 每次攻击都会在被攻击位置发出动静
 * 1个动静：对方所在的位置肯定在动静的相邻格（可以锁定对方就在4格内）
 * 2个动静：对方要么连走跑了，要么攻击完走了（可以锁定对方就在8格内）
 * 棋子被攻击命中就判负
 * 第26回合开始，每回合棋盘最外圈随机多出一个墙，棋盘没了还分不出胜负时双方均判负
 */
public class KnifeToDarkRoom extends Room {

    private final static String BLACK_MOVE_SIGN_TEXT = "?";
    private final static String WHITE_MOVE_SIGN_TEXT = "?";
    private final static String BLACK_ATTACK_SIGN_TEXT = "?";
    private final static String WHITE_ATTACK_SIGN_TEXT = "?";
    private final static String BLACK_KNIFE_SIGN_TEXT = "D";
    private final static String WHITE_KNIFE_SIGN_TEXT = "D";
    private final static String BLACK_MOVE_SIGN_COLOR = "#FF222222";
    private final static String WHITE_MOVE_SIGN_COLOR = "#FFDDDDDD";
    private final static String BLACK_ATTACK_SIGN_COLOR = "#FF222222";
    private final static String WHITE_ATTACK_SIGN_COLOR = "#FFDDDDDD";
    private final static String BLACK_KNIFE_SIGN_COLOR = "#FF222222";
    private final static String WHITE_KNIFE_SIGN_COLOR = "#FFDDDDDD";
    public static final int MAX_ACTION_COUNT = 2;

    private Assassin blackAssassin;
    private Assassin whiteAssassin;

    public KnifeToDarkRoom(String roomName) {
        super(roomName, "原作纸上游戏吧涅槃火凤凰的作品“向黑暗挥刀！”，江柯对其进行了一点改编。","暗影挥刀棋", BoardStyleData.getGoBangStyle(5,5), false, true);
    }

    @Override
    public String getRuleText() {
        return "该棋双方只能看到己方棋子和对方棋子造成的影子，观众也只能看到影子。\n" +
                "开局双方可选择任意格子落下1颗棋子，可以落子相同的位置，之后每回合轮流行动。\n" +
                "(点击棋盘下方按钮可以随时随地切行动状态)\n" +
                "可以选择移动和攻击1次，没有顺序限制。也可以连续移动或连续攻击2次。\n" +
                "移动只能上下左右移动1格，并在原位置留下影子。\n" +
                "（可以移动到对方棋子的位置上，毕竟你也看不到对方的棋子，也可以移动到影子上）\n" +
                "攻击也只能攻击相邻的1格上，并在该格子上留下影子。如果攻击的格子上有对方棋子[直接获胜] \n" +
                "为了避免开局就击杀结束了，增加限制，双方落子后的第一回合都不能进行攻击。";
    }

    @Override
    protected void onStartPrepare() {
        blackAssassin = null;
        whiteAssassin = null;
        noticeBlackMove();
        noticeAllBroadcast("请黑方先放置一颗棋子");
    }

    @Override
    public void resetGrids() {
        super.resetGrids();
        blackAssassin = null;
        whiteAssassin = null;
    }

    @Override
    public void noticeOnSeatReConnectMove(Player player) {
        if (cur == 1) {
            player.sendMessage(Msger.youCanMove, "Ok");
        }
        else if (cur == 2) {
            player.sendMessage(Msger.youCanMove, "Ok");
        }
        else if (cur == 3) {
            player.sendMessage(Msger.youCanMove, "Ok");
        }
        else {
            player.sendMessage(Msger.youCanMove, "Ok");
        }

        if (player_black == player) {
            String blackGameInfo = getBlackGameInfo();
            player_black.sendMessage(Msger.refreshGameInfo, blackGameInfo);
        }
        else if (player_white == player) {
            String whiteGameInfo = getWhiteGameInfo();
            player_white.sendMessage(Msger.refreshGameInfo, whiteGameInfo);
        }

        noticePlayerCurrentCountDown(player);
    }

    @Override
    public void noticeNoMove() {
        super.noticeNoMove();
        clearBottoms();
    }

    @Override
    public void noticeBlackMove() {
        blackBottoms();
        super.noticeBlackMove();
    }

    @Override
    public void noticeWhiteMove() {
        whiteBottoms();
        super.noticeWhiteMove();
    }

    private void blackBottoms() {
        if (player_black != null) {
            if (blackAssassin != null) {
                player_black.registerCustomBottom(new CustomBottom(this, "移动", (player) -> {
                    blackAssassin.changeMoveAction();
                    noticeBlackMove();
                }));
                if (step > 3) { //双方落子完算2步，第3步是黑方落子后的第一步，不能攻击
                    player_black.registerCustomBottom(new CustomBottom(this, "攻击", (player) -> {
                        blackAssassin.changeAttackAction();
                        noticeBlackMove();
                    }));
                }
            }
        }
        if (player_white != null) {
            player_white.clearCustomBottom();
            player_white.dispatchCustomBottom();
        }
    }

    private void whiteBottoms() {
        if (player_black != null) {
            player_black.clearCustomBottom();
            player_black.dispatchCustomBottom();
        }
        if (player_white != null) {
            if (whiteAssassin != null) {
                player_white.registerCustomBottom(new CustomBottom(this, "移动", (player) -> {
                    whiteAssassin.changeMoveAction();
                    noticeWhiteMove();
                }));
                if (step > 4) {
                    player_white.registerCustomBottom(new CustomBottom(this, "攻击", (player) -> {
                        whiteAssassin.changeAttackAction();
                        noticeWhiteMove();
                    }));
                }
            }
        }
    }

    private void clearBottoms() {
        if (player_black != null) {
            player_black.clearCustomBottom();
            player_black.dispatchCustomBottom();
        }
        if (player_white != null) {
            player_white.clearCustomBottom();
            player_white.dispatchCustomBottom();
        }
    }

    @Override
    public void doRule(int row, int col, int value) {
        if (blackAssassin == null) {
            if (value == 1) { //黑方初始落子
                blackAssassin = new Assassin(this, 1, row, col);
                stepAdd();;
                noticeWhiteMove();
                noticeAllBroadcast("请白方放置一颗棋子");
            }
        }
        else if (whiteAssassin == null) {
            if (value == 2) { //白方初始落子
                whiteAssassin = new Assassin(this, 2, row, col);
                stepAdd();;
                noticeBlackMove();
                noticeAllBroadcast("黑方开始轮流行动");
            }
        }
        else {
            if (value == 1) { //黑方行动
                int dis = getDistance(row, col, blackAssassin.pRow, blackAssassin.pCol);
                if (blackAssassin.actionMode == 0) { //如果黑方是移动状态
                    if (dis == 1) { //移动距离为1
                        blackAssassin.moveTo(row, col);//消耗一点行动力实际移动
                        if (blackAssassin.hasActionCount()) {
                            noticeBlackMove(); //黑棋继续
                        }
                        else {
                            stepAdd();;
                            whiteAssassin.resetActionCount(); //白棋重置行动力以免接下来不能行动两次
                            noticeWhiteMove(); //换白棋行动
                        }
                    }
                }
                else if (blackAssassin.actionMode == 1) { //如果黑方是攻击状态
                    if (dis == 1) { //攻击距离为1
                        blackAssassin.attackTo(row, col); //消耗一点行动力实际攻击
                        if (getDistance(row, col, whiteAssassin.pRow, whiteAssassin.pCol) == 0) { //攻击命中白棋
                            stepAdd();;
                            doOver(WinMode.BLACK_WIN, player_black.getNickNameOrTemp() + "黑棋成功击杀" + player_white.getNickNameOrTemp() + "的白棋");
                        }
                        else { //没命中
                            if (blackAssassin.hasActionCount()) {
                                noticeBlackMove(); //黑棋继续
                            }
                            else {
                                stepAdd();;
                                whiteAssassin.resetActionCount(); //白棋重置行动力以免接下来不能行动两次
                                noticeWhiteMove(); //换白棋行动
                            }
                        }
                    }
                }
            }
            else if (value == 2) { //白方行动
                int dis = getDistance(row, col, whiteAssassin.pRow, whiteAssassin.pCol);
                if (whiteAssassin.actionMode == 0) {  //白方是移动状态
                    if (dis == 1) {
                        whiteAssassin.moveTo(row, col);
                        if (whiteAssassin.hasActionCount()) {
                            noticeWhiteMove();
                        }
                        else {
                            stepAdd();;
                            blackAssassin.resetActionCount();
                            noticeBlackMove();
                        }
                    }
                }
                else if (whiteAssassin.actionMode == 1) { //白方是攻击状态
                    if (dis == 1) {
                        whiteAssassin.attackTo(row, col); //消耗一点行动力实际攻击
                        if (getDistance(row, col, blackAssassin.pRow, blackAssassin.pCol) == 0) { //攻击命中黑棋
                            stepAdd();;
                            doOver(WinMode.WHITE_WIN, player_white.getNickNameOrTemp() + "白棋成功击杀" + player_black.getNickNameOrTemp() + "的黑棋");
                        }
                        else { //没命中
                            if (whiteAssassin.hasActionCount()) {
                                noticeWhiteMove();
                            }
                            else {
                                stepAdd();;
                                blackAssassin.resetActionCount();
                                noticeBlackMove();
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public void playerEnter(Player player) {
        removeSpectator(player);//先移出，避免因奇怪原因玩家多次进入，出现多个观众的问题
        addSpectator(player);
        player.setEnteringRoom(this);
        player.sendMessage(Msger.enterRoomSuccess, "Ok");
        player.sendMessage(Msger.refreshGameInfo, getSpectatorGameInfo()); //刚进来更新一下游客游戏数据
        noticeAllRefreshRoomInfo();
        DataManager.noticeAllRefreshRoomList();
        noticePlayerCurrentCountDown(player); //通知刚进来的用户显示当前的倒计时动画
        System.out.println(player.getNickNameOrTemp() + "进入" + getRoomName());
    }

    @Override
    public void playerReConnectEnter(Player player) {
        player.sendMessage(Msger.enterRoomSuccess, "Ok"); //通知你进入房间成功
        player.sendMessage(Msger.refreshPlayerInfo, player.getPlayerInfo()); //更新角色
        player.sendMessage(Msger.refreshRoomInfo, getRoomInfo()); //更新一下游戏数据
        System.out.println(player.getNickNameOrTemp() + "重连到" + getRoomName());
        boolean isPlayingStarted = player_black != null && player_white != null; //黑白位置都有玩家，说明对弈是开始的状态
        boolean isOnSeatPlayer = player == player_black || player == player_white; //重连的玩家属于在座玩家
        if (isPlayingStarted && isOnSeatPlayer) { //checkGameStart()中满足要求了会刷新界面，用这种方式防止重复刷新
            if (overFlag == 2 || overFlag == 3 || overFlag == 4) { //对弈结束状态成为棋手时，分发继续按钮
                overFlag = 2; //重进双方重新确认判断
                dispatchContinueButton();
            }
            noticeOnSeatReConnectMove(player);
        }
        else {
            if (player_black != null) {
                String blackGameInfo = getBlackGameInfo();
                player_black.sendMessage(Msger.refreshGameInfo, blackGameInfo);
            }
            else if (player_white != null) {
                String whiteGameInfo = getWhiteGameInfo();
                player_white.sendMessage(Msger.refreshGameInfo, whiteGameInfo);
            }
            else {
                String specGameInfo = getSpectatorGameInfo();
                player.sendMessage(Msger.refreshGameInfo, specGameInfo);
            }
        }
    }

    @Override
    protected void noticeAllRefreshGameInfo() {
        if (cur == 0) { //终局时，所有人都能看到双方棋子的全部信息
            String compGameInfo = getCompletelyGameInfo();
            if (player_black != null) {
                player_black.sendMessage(Msger.refreshGameInfo, compGameInfo);
            }
            if (player_white != null) {
                player_white.sendMessage(Msger.refreshGameInfo, compGameInfo);
            }
            for (Player player : spectatorList) {
                player.sendMessage(Msger.refreshGameInfo, compGameInfo);
            }
        }
        else { //正常对局时
            if (player_black != null) {
                String blackGameInfo = getBlackGameInfo();
                player_black.sendMessage(Msger.refreshGameInfo, blackGameInfo);
            }
            if (player_white != null) {
                String whiteGameInfo = getWhiteGameInfo();
                player_white.sendMessage(Msger.refreshGameInfo, whiteGameInfo);
            }
            if (spectatorList.size() > 0) {
                String specGameInfo = getSpectatorGameInfo();
                for (Player player : spectatorList) {
                    player.sendMessage(Msger.refreshGameInfo, specGameInfo);
                }
            }
        }
    }

    @Override
    public String getCoordMode() {
        return "00000000";
    }

    //获取完全的游戏信息
    private String getCompletelyGameInfo() {
        int[][] compGrids = GridsUtils.copyGrids(curGrids);
        if (blackAssassin != null) {
            compGrids[blackAssassin.pRow][blackAssassin.pCol] = 1;
        }
        if (whiteAssassin != null) {
            compGrids[whiteAssassin.pRow][whiteAssassin.pCol] = 2;
        }
        List<Sign> compSigns = new ArrayList<>();
        if (blackAssassin != null) {
            compSigns.addAll(blackAssassin.getAllSigns());
        }
        if (whiteAssassin != null) {
            compSigns.addAll(whiteAssassin.getAllSigns());
        }
        return "" + step +
                "&" + cur +
                "&" + blackScore +
                "&" + whiteScore +
                "&" + gameTip +
                "&" + GridsUtils.grids2Str(compGrids) +
                "&" + SignsUtils.signs2Str(compSigns)+
                "&" + getCoordMode();
    }

    //获取黑方显示的游戏信息
    private String getBlackGameInfo() {
        int[][] compGrids = GridsUtils.copyGrids(curGrids);
        if (blackAssassin != null) {
            compGrids[blackAssassin.pRow][blackAssassin.pCol] = 1; //可以看到自己的棋子位置
        }
        List<Sign> compSigns = new ArrayList<>();
        if (blackAssassin != null) {
            compSigns.addAll(blackAssassin.getAllSigns()); //可以看到自己的全部标志
        }
        if (whiteAssassin != null) {
            if (!whiteAssassin.hasActionCount()) {
                compSigns.addAll(whiteAssassin.getShadowSigns()); //看不到对方的提刀标志
            }
        }
        return "" + step +
                "&" + cur +
                "&" + blackScore +
                "&" + whiteScore +
                "&" + gameTip +
                "&" + GridsUtils.grids2Str(compGrids) +
                "&" + SignsUtils.signs2Str(compSigns)+
                "&" + getCoordMode();
    }

    //获取白方显示的游戏信息
    private String getWhiteGameInfo() {
        int[][] compGrids = GridsUtils.copyGrids(curGrids);
        if (whiteAssassin != null) {
            compGrids[whiteAssassin.pRow][whiteAssassin.pCol] = 2;
        }
        List<Sign> compSigns = new ArrayList<>();
        if (whiteAssassin != null) {
            compSigns.addAll(whiteAssassin.getAllSigns()); //可以看到自己的全部标志
        }
        if (blackAssassin != null) {
            if (!blackAssassin.noCompleteAction()) {
                compSigns.addAll(blackAssassin.getShadowSigns()); //看不到对方的提刀标志
            }
        }
        return "" + step +
                "&" + cur +
                "&" + blackScore +
                "&" + whiteScore +
                "&" + gameTip +
                "&" + GridsUtils.grids2Str(compGrids) +
                "&" + SignsUtils.signs2Str(compSigns)+
                "&" + getCoordMode();
    }

    //获取观众显示的游戏信息
    private String getSpectatorGameInfo() {
        int[][] compGrids = GridsUtils.copyGrids(curGrids); //观众看不到双方棋子位置
        List<Sign> compSigns = new ArrayList<>(); //观众只能看到影子
        if (blackAssassin != null) {
            compSigns.addAll(blackAssassin.getShadowSigns()); //可以看到自己的全部标志
        }
        if (whiteAssassin != null) {
            compSigns.addAll(whiteAssassin.getShadowSigns()); //看不到对方的提刀标志
        }
        return "" + step +
                "&" + cur +
                "&" + blackScore +
                "&" + whiteScore +
                "&" + gameTip +
                "&" + GridsUtils.grids2Str(compGrids) +
                "&" + SignsUtils.signs2Str(compSigns)+
                "&" + getCoordMode();
    }

    //获取两个点之间的距离
    private int getDistance(int aRow, int aCol, int bRow, int bCol) {
        return Math.abs(aRow - bRow) + Math.abs(aCol - bCol);
    }

    private static class Assassin {

        KnifeToDarkRoom mRoom; //外部房间对象的引用
        int mValue; //黑棋还是白棋
        int pRow; //在棋盘上的横坐标
        int pCol; //在棋盘上的纵坐标
        int actionMode; //当前行动模式  0:移动  1:攻击
        int actionCount; //当前可行动次数
        List<Sign> signList; //该棋子的标记

        TextSign knifeSign; //索引提刀状态的标记

        public Assassin(KnifeToDarkRoom room, int value, int row, int col) {
            this.mRoom = room;
            this.mValue = value;
            this.pRow = row;
            this.pCol = col;
            this.actionMode = 0;
            this.actionCount = MAX_ACTION_COUNT;
            this.signList = new ArrayList<>();
        }

        public List<Sign> getAllSigns() {
            return signList;
        }

        public List<Sign> getShadowSigns() {
            List<Sign> signs = new ArrayList<>(signList);
            signs.remove(knifeSign);
            return signs;
        }

        private void showKnifeSign() {
            signList.remove(knifeSign);
            if (mValue == 1) {
                knifeSign = new TextSign(mRoom.getIndex(pRow, pCol), BLACK_KNIFE_SIGN_TEXT, BLACK_KNIFE_SIGN_COLOR);
            }
            else {
                knifeSign = new TextSign(mRoom.getIndex(pRow, pCol), WHITE_KNIFE_SIGN_TEXT, WHITE_KNIFE_SIGN_COLOR);
            }
            signList.add(knifeSign);
        }

        private void dismissKnifeSign() {
            signList.remove(knifeSign);
            knifeSign = null;
        }

        public void changeMoveAction() {
            actionMode = 0;
            dismissKnifeSign();
        }

        public void changeAttackAction() {
            actionMode = 1;
            showKnifeSign();
        }

        public void resetActionCount() {
            actionCount = MAX_ACTION_COUNT;
        }

        public boolean hasActionCount() {
            return actionCount > 0;
        }

        /**
         * 表示该棋子有过行动了，但是没有全部行动完
         */
        public boolean noCompleteAction() {
            return actionCount != 0 && actionCount != MAX_ACTION_COUNT;
        }

        public void moveTo(int row, int col) {
            if (actionCount == MAX_ACTION_COUNT) { //行动是满的说明是新行动
                clearShadowSigns(); //移除所有影子
            }
            addMoveSign(pRow, pCol);
            pRow = row;
            pCol = col;
            actionCount--;
        }

        private void addMoveSign(int row, int col) {
            TextSign textSign;
            if (mValue == 1) {
                textSign = new TextSign(mRoom.getIndex(row, col), BLACK_MOVE_SIGN_TEXT, BLACK_MOVE_SIGN_COLOR);
            }
            else {
                textSign = new TextSign(mRoom.getIndex(row, col), WHITE_MOVE_SIGN_TEXT, WHITE_MOVE_SIGN_COLOR);
            }
            signList.add(textSign);
        }

        public void attackTo(int row, int col) {
            if (actionCount == MAX_ACTION_COUNT) { //行动是满的说明是新行动
                clearShadowSigns(); //移除所有影子
            }
            addAttackSign(row, col);
            actionCount--;
        }

        private void addAttackSign(int row, int col) {
            TextSign textSign;
            if (mValue == 1) {
                textSign = new TextSign(mRoom.getIndex(row, col), BLACK_ATTACK_SIGN_TEXT, BLACK_ATTACK_SIGN_COLOR);
            }
            else {
                textSign = new TextSign(mRoom.getIndex(row, col), WHITE_ATTACK_SIGN_TEXT, WHITE_ATTACK_SIGN_COLOR);
            }
            signList.add(textSign);
        }

        private void clearShadowSigns() {
            signList.clear();
            if (knifeSign != null) {
                signList.add(knifeSign);
            }
        }
    }
}
